package cn.mesmile.lock.aop;

import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author zb
 * @date 2020/12/1 15:31
 * @Description
 */
@Slf4j
@Aspect
@Component
@Order(1)   //order越小越是最先执行，但更重要的是最先执行的最后结束。order默认值是2147483647
public class LockAspect implements ApplicationContextAware {

    private final String LOCK_CODE = "lockCode";

    private final RedissonClient redissonClient;

    private ApplicationContext applicationContext;

    public LockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     *  定义公共的切点
     */
    @Pointcut("@annotation(LockAction)")
    public void log(){

    }

    /**
     *  环绕通知
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(lockAction)")
    public Object around(ProceedingJoinPoint joinPoint,LockAction lockAction) throws Throwable {

        String code = IdUtil.fastSimpleUUID();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String remoteUser = request.getRemoteUser();
        log.info(">>>>>>>>>>>> remoteUser 远程用户：{}",remoteUser);
        String requestURI = request.getRequestURI();
        log.info(">>>>>>>>>>>> requestURI 请求地址：{}",requestURI);
        request.setAttribute(LOCK_CODE, code);

        // 默认，可重入锁
        RLock lock = redissonClient.getLock(lockAction.value());
        Object proceed = null;
        try {
            // 尝试锁住    【获取锁的最大等待时间 60s】  【锁租期 100s】 时间单位：秒  【谨慎用 异步 的方式】
//            RFuture<Boolean> booleanRFuture = lock.tryLockAsync(lockAction.waitTime(), lockAction.leaseTime(), lockAction.timeUnit());
            boolean tryLock = lock.tryLock(lockAction.waitTime(), lockAction.leaseTime(), lockAction.timeUnit());
//            lock.lock(); // 【这种方式最稳妥】
            if (tryLock) {
                // 类似于  method.invoke 方法
                System.out.println(">>>>>>>>>>>>>>>>>>> 加锁成功");
                proceed = joinPoint.proceed();
            }
        } finally {
            lock.unlock();
            System.out.println(">>>>>>>>>>>>>>>>>>> 解锁成功");
        }

        // 第一个参数
//        String seckillId = joinPoint.getArgs()[0].toString();
        // 方法名
//        String name = joinPoint.getSignature().getName();
//        Class declaringType = joinPoint.getSignature().getDeclaringType();
//        Method[] methods = declaringType.getMethods();
//        Object proceed = null;
//        for (Method method : methods) {
//            if(name.equals(method.getName())){
//                // 获得方法上面的注解
//                String value = AnnotationUtil
//                        .getAnnotationValue(method, LockAction.class, "value");
//                if (StrUtil.isEmpty(value)){
//                    throw new RuntimeException("运行时发生异常");
//                }
//                // 获取锁
//                RLock lock = redissonClient.getLock(value);
//                try {
//                    // 加锁,加锁结果
////                    lock.lock();
////                    boolean tryLock = lock.tryLock();
//                    boolean tryLock = lock.tryLock(lockAction.waitTime(), lockAction.leaseTime(), lockAction.timeUnit());
//                    if (tryLock){
//                        System.out.println(">>>>>>>>>>>>>>>>>>> 枷锁成功");
//                        // 类似于  method.invoke 方法
//                        proceed = joinPoint.proceed();
//                    }
//                } finally {
//                    System.out.println(">>>>>>>>>>>>>>>>  解除锁定");
//                    lock.unlock();
//                }
//            }
//        }
        return proceed;
    }

    /**
     *  异常通知，当目标方法抛出异常时，该方法会被触发
     * @param joinPoint
     */
    @AfterThrowing(value = "log()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String code = (String) request.getAttribute(LOCK_CODE);
        log.info(">>>>>>>>>>  错误码：{}", code);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
